// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-02-01 // namespace LargoCommon.Music { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using Abstract; using LargoCommon.Interfaces; /// /// Musical Linearizer. /// public class MusicalLinearizer { #region Fields /// /// Melodic tones. /// private static IList melodicTones; /// /// Initializes a new instance of the class. /// /// The given header. public MusicalLinearizer(MusicalHeader givenHeader) { this.Header = givenHeader; this.Parts = new List(); } #endregion #region Properties /// /// Gets or sets the header. /// /// /// The header. /// public MusicalHeader Header { get; set; } /// /// Gets or sets Musical Lines. /// /// Property description. public IList Lines { get; set; } /// /// Gets or sets Musical Lines. /// /// Property description. public IList Parts { get; set; } /// /// Gets or sets the total duration. /// /// /// The total duration. /// private long TotalDuration { get; set; } #endregion #region Public static /// /// Line Of Tones. /// /// Source track. /// Voice number. /// Returns value. public static MusicalLine NextRhythmicLineTrack(MusicalLine sourceTrack, byte voice) { Contract.Requires(sourceTrack != null); if (sourceTrack == null || sourceTrack.IsEmpty) { return null; } var track = (MusicalLine)sourceTrack.Clone(); if (!((from tone in sourceTrack.Tones where tone.ToneType == MusicalToneType.Rhythmic && !tone.IsPause && !tone.IsReady orderby tone.BarNumber select tone).FirstOrDefault() is MusicalStrike firstTone)) { return null; } //// Key Number represents Instrument but why is not here LineType = MusicalLineType.Rhythmic? //// if (track.Channel == (byte)MidiChannel.DrumChannel) { //// track.Status.GChannel = new GeneralChannel(InstrumentGenus.Melodical, firstTone.Instrument, MidiChannel.DrumChannel); track.FirstStatus.Instrument = new MusicalInstrument((MidiMelodicInstrument)firstTone.InstrumentNumber); track.FirstStatus.LineType = MusicalLineType.Rhythmic; var lastTone = firstTone; var optimalTone = firstTone; //// if (optimalTone == null) { return null; } optimalTone.IsReady = true; optimalTone.Staff = sourceTrack.LineNumber; optimalTone.Voice = voice; track.Tones.Add(optimalTone); foreach ( var tone in sourceTrack.Tones.Where(tone => tone.ToneType == MusicalToneType.Rhythmic && !tone.IsPause && !tone.IsReady)) { optimalTone = (tone.InstrumentNumber == lastTone.InstrumentNumber ? tone : null) as MusicalStrike; if (optimalTone == null) { continue; } optimalTone.IsReady = true; optimalTone.Staff = sourceTrack.LineNumber; optimalTone.Voice = voice; track.Tones.Add(optimalTone); lastTone = optimalTone; } track.FirstStatus.Staff = sourceTrack.LineNumber; track.FirstStatus.Voice = voice; return track; } #endregion #region String representation /// String representation of the object. /// Returns value. public override string ToString() { return "Musical Linearizer"; } #endregion #region Public methods /// /// Line Of Tones. /// /// Source track. /// Voice number. /// Returns value. public MusicalLine NextMelodicLineTrack(MusicalLine sourceTrack, byte voice) { Contract.Requires(sourceTrack != null); if (sourceTrack == null || sourceTrack.IsEmpty) { return null; } var track = (MusicalLine)sourceTrack.Clone(); melodicTones = sourceTrack.Tones .Where(tone => tone.ToneType == MusicalToneType.Melodic && !tone.IsPause && !tone.IsReady) .ToList(); if (!((from tone in melodicTones orderby tone.BarNumber select tone).FirstOrDefault() is MusicalTone firstTone)) { return null; } //// Key Number represents Instrument but why is not here LineType = MusicalLineType.Rhythmic? //// if (track.Channel == (byte)MidiChannel.DrumChannel) { //// track.Instrument = firstTone.Pitch.MidiKeyNumber(); //// track.LineType = MusicalLineType.Rhythmic; } var lastTone = firstTone; var optimalTone = firstTone; optimalTone.IsReady = true; optimalTone.Staff = sourceTrack.LineNumber; optimalTone.Voice = voice; track.Tones.Add(optimalTone); while (true) { optimalTone = FindOptimalTone(lastTone); if (optimalTone == null) { break; } MusicalStrike.CorrectBadBinding(lastTone, optimalTone); optimalTone.IsReady = true; optimalTone.Staff = sourceTrack.LineNumber; optimalTone.Voice = voice; track.Tones.Add(optimalTone); lastTone = optimalTone; } track.FirstStatus.Staff = sourceTrack.LineNumber; track.FirstStatus.Voice = voice; var completedTones = track.Tones.CollectionWithAddedMissingPauses(); var standardizedTones = completedTones.StandardizeTones(this.Header); track.SetTones(standardizedTones); return track; } /// /// Transfer Parts To Lines. /// /// if set to true [skip negligible tracks]. public void TransferPartsToTracks(bool skipNegligibleTracks) { //// MusicalBlock block, block.TotalDuration this.Lines = new List(); int lineIndex = 0; foreach (var part in this.Parts) { // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (var track in part.MusicalLines) { //// this.MusicalParts.Select(part => part.MusicalTracks).SelectMany(partTracks => partTracks)) { //// Test 2013/12 //// Skip negligible tracks !? if (skipNegligibleTracks) { var minimumDuration = this.TotalDuration / 10; if (track.DurationOfTones < minimumDuration) { continue; } } track.LineIndex = lineIndex++; track.FirstStatus.BarNumber = 1; //// 2018/12 track.FirstStatus.Instrument = part.Instrument; track.MainVoice.Channel = part.Channel; //// track.Status.GChannel = new GeneralChannel(InstrumentGenus.Melodical, part.Instrument, part.Channel); var tracks = (List)this.Lines; tracks.Add(track); } } //// 2016 this.NumberOfRhythmicLines = 0; //// 2016 this.NumberOfMelodicLines = lineIndex; } /// /// Split Lines To Parts. /// /// The block. /// If set to true [split multi-voice tracks]. public void SplitTracksToParts(MusicalBlock block, FileSplit splitMultiVoiceTracks) { const float minimalOccupationForSplit = 1.1f; const float minimalOccupationForInclusion = 0.05f; //// 2015/01 this.TotalDuration = block.TotalDuration; this.Parts = new List(); byte voice = 0; byte staff = 0; foreach (var sourceTrack in this.Lines) { if (sourceTrack.LineNumber != staff) { staff = sourceTrack.LineNumber; voice = 0; } var part = new MusicalPart(block) { Channel = sourceTrack.MainVoice.Channel, Instrument = sourceTrack.FirstStatus.Instrument //// 2019/10 }; //// Melodic track if (sourceTrack.FirstStatus.IsMelodic) { var q = sourceTrack.QuotientOfOccupation(); if (splitMultiVoiceTracks == FileSplit.Total || (splitMultiVoiceTracks == FileSplit.Automatic && q > minimalOccupationForSplit)) { while (true) { var track = this.NextMelodicLineTrack(sourceTrack, voice++); if (track == null) { break; } q = track.QuotientOfOccupation(); if (q > minimalOccupationForInclusion) { part.MusicalLines.Add(track); } } } else { part.MusicalLines.Add(sourceTrack); } } //// Rhythmic track if (sourceTrack.FirstStatus.IsRhythmic) { while (true) { var line = NextRhythmicLineTrack(sourceTrack, voice++); if (line == null) { break; } part.MusicalLines.Add(line); } } //// If there is more sequence in one part, decrement their loudness !? //// DecrementLoudness(part); this.Parts.Add(part); } } #endregion #region Private static methods /// /// Finds the optimum tone. /// /// The last tone. /// Returns value. private static MusicalTone FindOptimalTone(MusicalTone lastTone) { Contract.Requires(lastTone != null); const int maxPassTones = 12; MusicalTone optimalTone = null; var optimalValue = 0; var countPassed = 0; foreach (var musicalStrike in melodicTones) { var mtone = (MusicalTone)musicalStrike; //// .Select(tone => tone as MusicalTone) var continuous = true; if (mtone.IsReady) { continue; } var value = 100; //// Here possibly toleration of some percent of length var minStartPosition = lastTone.BitPosition + (int)Math.Round(lastTone.Duration * 0.8f); //// 0.9f if (mtone.BitPosition >= minStartPosition) { var dist = mtone.Pitch.DistanceFrom(lastTone.Pitch); if (dist == 0) { value += 10; } else { value -= dist; if (dist > DefaultValue.HarmonicOrder) { value -= 1; } } var bitDistance = Math.Abs(mtone.BitPosition - lastTone.BitPosition); value -= bitDistance / mtone.BitRange.Order; if (mtone.BitRange.Length == mtone.BitRange.Order && lastTone.BitRange.Length == lastTone.BitRange.Order) { value += 1; } if (lastTone.BarNumber == mtone.BarNumber - 1 && lastTone.IsGoingToNextBar != mtone.IsFromPreviousBar) { value -= 100; continuous = false; } if (value > optimalValue && (!continuous || optimalTone == null || mtone.BitPosition <= optimalTone.BitPosition)) { //// || optimalValue < -500 optimalValue = value; optimalTone = mtone; } countPassed++; } if (countPassed > maxPassTones) { break; } } return optimalTone; } #endregion } }